home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Best of Shareware
/
Best of PC Windows Shareware 1.0 - Wayzata Technology (7111) (1993).iso
/
mac
/
ZIPPED
/
DOS
/
GRAPHICS
/
RAYSH386.ZIP
/
SRC
/
TRIANGLE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-04-30
|
13KB
|
475 lines
/*
* triangle.c
*
* Copyright (C) 1989, 1991, Craig E. Kolb
* All rights reserved.
*
* This software may be freely copied, modified, and redistributed
* provided that this copyright notice is preserved on all copies.
*
* You may not distribute this software, in whole or in part, as part of
* any commercial product without the express consent of the authors.
*
* There is no warranty or other guarantee of fitness of this software
* for any purpose. It is provided solely "as is".
*
* $Id: triangle.c,v 4.0.1.1 91/09/29 15:47:11 cek Exp Locker: cek $
*
* $Log: triangle.c,v $
* Revision 4.0.1.1 91/09/29 15:47:11 cek
* patch1: Potential roundoff problem in dPdU code.
*
* Revision 4.0 91/07/17 14:39:38 kolb
* Initial version.
*
*/
#include "geom.h"
#include "triangle.h"
static Methods *iTriangleMethods = NULL;
static char triName[] = "triangle";
unsigned long TriTests, TriHits;
static void TriangleSetdPdUV();
/*
* Create and return reference to a triangle.
*/
Triangle *
TriangleCreate(type, p1, p2, p3, n1, n2, n3, u1, u2, u3, flipflag)
int type;
Vector *p1, *p2, *p3, *n1, *n2, *n3;
Vec2d *u1, *u2, *u3;
int flipflag;
{
Triangle *triangle;
Vector ptmp, anorm;
Float d;
/*
* Allocate new triangle and primitive to point to it.
*/
triangle = (Triangle *)share_malloc(sizeof(Triangle));
triangle->type = type; /* so inttri can tell the difference */
VecSub(*p2, *p1, &triangle->e[0]);
VecSub(*p3, *p2, &triangle->e[1]);
VecSub(*p1, *p3, &triangle->e[2]);
/* Find plane normal. */
VecCross(&triangle->e[0], &triangle->e[1], &ptmp);
triangle->nrm = ptmp;
if (VecNormalize(&triangle->nrm) == 0.) {
RLerror(RL_ADVISE, "Degenerate triangle.\n");
return (Triangle *)NULL;
}
if (flipflag)
VecScale(-1, triangle->nrm, &triangle->nrm);
triangle->d = dotp(&triangle->nrm, p1);
triangle->p[0] = *p1;
triangle->p[1] = *p2;
triangle->p[2] = *p3;
if (type == PHONGTRI) {
if (VecNormalize(n1) == 0. || VecNormalize(n2) == 0. ||
VecNormalize(n3) == 0.) {
RLerror(RL_WARN, "Degenerate vertex normal.\n");
return (Triangle *)NULL;
}
triangle->vnorm = (Vector *)RayMalloc(3 * sizeof(Vector));
triangle->vnorm[0] = *n1;
triangle->vnorm[1] = *n2;
triangle->vnorm[2] = *n3;
if (flipflag) {
/* Flip the vertex normals */
VecScale(-1, triangle->vnorm[0],
&triangle->vnorm[0]);
VecScale(-1, triangle->vnorm[1],
&triangle->vnorm[1]);
VecScale(-1, triangle->vnorm[2],
&triangle->vnorm[2]);
} else if (dotp(&triangle->vnorm[0], &triangle->nrm) < 0.) {
/*
* Reverse direction of surface normal on Phong
* triangle if the surface normal points "away"
* from the first vertex normal.
* Note that this means that we trust the vertex
* normals rather than trust that the user gave the
* vertices in the correct order.
*/
RLerror(RL_ADVISE, "Inconsistant triangle normals.\n");
VecScale(-1., triangle->nrm, &triangle->nrm);
VecScale(-1., ptmp, &ptmp);
triangle->d = -triangle->d;
VecScale(-1., triangle->e[0], &triangle->e[0]);
VecScale(-1., triangle->e[1], &triangle->e[1]);
VecScale(-1., triangle->e[2], &triangle->e[2]);
}
}
/*
* If UV coordinates are given for the vertices, allocate and
* store them.
*/
if (u1 && u2 && u3) {
triangle->uv = (Vec2d *)RayMalloc(3 * sizeof(Vec2d));
triangle->uv[0] = *u1;
triangle->uv[1] = *u2;
triangle->uv[2] = *u3;
/* Calculate dpdu and dpdv vectors */
triangle->dpdu = (Vector *)RayMalloc(sizeof(Vector));
triangle->dpdv = (Vector *)RayMalloc(sizeof(Vector));
TriangleSetdPdUV(triangle->p, triangle->uv,
triangle->dpdu, triangle->dpdv);
} else {
triangle->uv = (Vec2d *)NULL;
}
/*
* Find "dominant" part of normal vector.
*/
anorm.x = fabs(ptmp.x);
anorm.y = fabs(ptmp.y);
anorm.z = fabs(ptmp.z);
/*
* Scale edges by dominant part of normal. This makes intersection
* testing a bit faster.
*/
if (anorm.x > anorm.y && anorm.x > anorm.z) {
triangle->index = XNORMAL;
d = 1. / ptmp.x;
} else if (anorm.y > anorm.z) {
triangle->index = YNORMAL;
d = 1. / ptmp.y;
} else {
triangle->index = ZNORMAL;
d = 1. /ptmp.z;
}
VecScale(d, triangle->e[0], &triangle->e[0]);
VecScale(d, triangle->e[1], &triangle->e[1]);
VecScale(d, triangle->e[2], &triangle->e[2]);
return triangle;
}
Methods *
TriangleMethods()
{
if (iTriangleMethods == (Methods *)NULL) {
iTriangleMethods = MethodsCreate();
iTriangleMethods->create = (GeomCreateFunc *)TriangleCreate;
iTriangleMethods->methods = TriangleMethods;
iTriangleMethods->name = TriangleName;
iTriangleMethods->intersect = TriangleIntersect;
iTriangleMethods->normal = TriangleNormal;
iTriangleMethods->uv = TriangleUV;
iTriangleMethods->bounds = TriangleBounds;
iTriangleMethods->stats = TriangleStats;
iTriangleMethods->checkbounds = TRUE;
iTriangleMethods->closed = FALSE;
}
return iTriangleMethods;
}
/*
* Intersect ray with triangle. This is an optimized version of the
* intersection routine from Snyder and Barr's '87 SIGGRAPH paper.
*/
int
TriangleIntersect(tri, ray, mindist, maxdist)
Triangle *tri;
Ray *ray;
Float mindist, *maxdist;
{
Float qi1, qi2, s, k, b0, b1, b2;
Vector pos, dir;
TriTests++;
pos = ray->pos;
dir = ray->dir;
/*
* Plane intersection.
*/
k = dotp(&tri->nrm, &dir);
if (fabs(k) < EPSILON)
return FALSE;
s = (tri->d - dotp(&tri->nrm, &pos)) / k;
if (s < mindist || s > *maxdist)
return FALSE;
if (tri->index == XNORMAL) {
qi1 = pos.y + s * dir.y;
qi2 = pos.z + s * dir.z;
b0 = tri->e[1].y * (qi2 - tri->p[1].z) -
tri->e[1].z * (qi1 - tri->p[1].y);
if (b0 < 0. || b0 > 1.)
return FALSE;
b1 = tri->e[2].y * (qi2 - tri->p[2].z) -
tri->e[2].z * (qi1 - tri->p[2].y);
if (b1 < 0. || b1 > 1.)
return FALSE;
b2 = tri->e[0].y * (qi2 - tri->p[0].z) -
tri->e[0].z * (qi1 - tri->p[0].y);
if (b2 < 0. || b2 > 1.)
return FALSE;
} else if (tri->index == YNORMAL) {
qi1 = pos.x + s * dir.x;
qi2 = pos.z + s * dir.z;
b0 = tri->e[1].z * (qi1 - tri->p[1].x) -
tri->e[1].x * (qi2 - tri->p[1].z);
if (b0 < 0. || b0 > 1.)
return FALSE;
b1 = tri->e[2].z * (qi1 - tri->p[2].x) -
tri->e[2].x * (qi2 - tri->p[2].z);
if (b1 < 0. || b1 > 1.)
return FALSE;
b2 = tri->e[0].z * (qi1 - tri->p[0].x) -
tri->e[0].x * (qi2 - tri->p[0].z);
if (b2 < 0. || b2 > 1.)
return FALSE;
} else {
qi1 = pos.x + s * dir.x;
qi2 = pos.y + s * dir.y;
b0 = tri->e[1].x * (qi2 - tri->p[1].y) -
tri->e[1].y * (qi1 - tri->p[1].x);
if (b0 < 0. || b0 > 1.)
return FALSE;
b1 = tri->e[2].x * (qi2 - tri->p[2].y) -
tri->e[2].y * (qi1 - tri->p[2].x);
if (b1 < 0. || b1 > 1.)
return FALSE;
b2 = tri->e[0].x * (qi2 - tri->p[0].y) -
tri->e[0].y * (qi1 - tri->p[0].x);
if (b2 < 0. || b2 > 1.)
return FALSE;
}
tri->b[0] = b0;
tri->b[1] = b1;
tri->b[2] = b2;
TriHits++;
*maxdist = s;
return TRUE;
}
int
TriangleNormal(tri, pos, nrm, gnrm)
Triangle *tri;
Vector *pos, *nrm, *gnrm;
{
*gnrm = tri->nrm;
if (tri->type == FLATTRI) {
*nrm = tri->nrm;
return FALSE;
}
/*
* Interpolate normals of Phong-shaded triangles.
*/
nrm->x = tri->b[0]*tri->vnorm[0].x+tri->b[1]*tri->vnorm[1].x+
tri->b[2]*tri->vnorm[2].x;
nrm->y = tri->b[0]*tri->vnorm[0].y+tri->b[1]*tri->vnorm[1].y+
tri->b[2]*tri->vnorm[2].y;
nrm->z = tri->b[0]*tri->vnorm[0].z+tri->b[1]*tri->vnorm[1].z+
tri->b[2]*tri->vnorm[2].z;
(void)VecNormalize(nrm);
return TRUE;
}
/*ARGSUSED*/
void
TriangleUV(tri, pos, norm, uv, dpdu, dpdv)
Triangle *tri;
Vector *pos, *norm, *dpdu, *dpdv;
Vec2d *uv;
{
Float d;
/*
* Normalize barycentric coordinates.
*/
d = tri->b[0]+tri->b[1]+tri->b[2];
tri->b[0] /= d;
tri->b[1] /= d;
tri->b[2] /= d;
if (dpdu) {
if (tri->uv == (Vec2d *)NULL) {
*dpdu = tri->e[0];
(void)VecNormalize(dpdu);
VecSub(tri->p[0], *pos, dpdv);
(void)VecNormalize(dpdv);
} else {
*dpdu = *tri->dpdu;
*dpdv = *tri->dpdv;
}
}
if (tri->uv == (Vec2d *)NULL) {
uv->v = tri->b[2];
if (equal(uv->v, 1.))
uv->u = 0.;
else
uv->u = tri->b[1] / (tri->b[0] + tri->b[1]);
} else {
/*
* Compute UV by taking weighted sum of UV coordinates.
*/
uv->u = tri->b[0]*tri->uv[0].u + tri->b[1]*tri->uv[1].u +
tri->b[2]*tri->uv[2].u;
uv->v = tri->b[0]*tri->uv[0].v + tri->b[1]*tri->uv[1].v +
tri->b[2]*tri->uv[2].v;
}
}
void
TriangleBounds(tri, bounds)
Triangle *tri;
Float bounds[2][3];
{
bounds[LOW][X] = bounds[HIGH][X] = tri->p[0].x;
bounds[LOW][Y] = bounds[HIGH][Y] = tri->p[0].y;
bounds[LOW][Z] = bounds[HIGH][Z] = tri->p[0].z;
if (tri->p[1].x < bounds[LOW][X]) bounds[LOW][X] = tri->p[1].x;
if (tri->p[1].x > bounds[HIGH][X]) bounds[HIGH][X] = tri->p[1].x;
if (tri->p[2].x < bounds[LOW][X]) bounds[LOW][X] = tri->p[2].x;
if (tri->p[2].x > bounds[HIGH][X]) bounds[HIGH][X] = tri->p[2].x;
if (tri->p[1].y < bounds[LOW][Y]) bounds[LOW][Y] = tri->p[1].y;
if (tri->p[1].y > bounds[HIGH][Y]) bounds[HIGH][Y] = tri->p[1].y;
if (tri->p[2].y < bounds[LOW][Y]) bounds[LOW][Y] = tri->p[2].y;
if (tri->p[2].y > bounds[HIGH][Y]) bounds[HIGH][Y] = tri->p[2].y;
if (tri->p[1].z < bounds[LOW][Z]) bounds[LOW][Z] = tri->p[1].z;
if (tri->p[1].z > bounds[HIGH][Z]) bounds[HIGH][Z] = tri->p[1].z;
if (tri->p[2].z < bounds[LOW][Z]) bounds[LOW][Z] = tri->p[2].z;
if (tri->p[2].z > bounds[HIGH][Z]) bounds[HIGH][Z] = tri->p[2].z;
}
char *
TriangleName()
{
return triName;
}
void
TriangleStats(tests, hits)
unsigned long *tests, *hits;
{
*tests = TriTests;
*hits = TriHits;
}
/*
* Given three vertices of a triangle and the uv coordinates associated
* with each, compute directions of u and v axes.
*/
static void
TriangleSetdPdUV(p, t, dpdu, dpdv)
Vector p[3]; /* Triangle vertices */
Vec2d t[3]; /* uv coordinates for each vertex */
Vector *dpdu, *dpdv; /* u and v axes (return values) */
{
Float scale;
int hi, mid, lo;
Vector base;
/* sort u coordinates */
if (t[2].u > t[1].u) {
if (t[1].u > t[0].u) {
hi = 2; mid = 1; lo = 0;
} else if (t[2].u > t[0].u) {
hi = 2; mid = 0; lo = 1;
} else {
hi = 0; mid = 2; lo = 1;
}
} else {
if (t[2].u > t[0].u) {
hi = 1; mid = 2; lo = 0;
} else if (t[1].u > t[0].u) {
hi = 1; mid = 0; lo = 2;
} else {
hi = 0; mid = 1; lo = 2;
}
}
if (fabs(t[hi].u - t[lo].u) < EPSILON) {
/* degenerate axis */
dpdv->x = dpdv->y = dpdv->z = 0.;
} else {
/*
* Given u coordinates of vertices forming the
* 'long' edge, find where 'middle'
* vertex falls on that edge given its u coordinate.
*/
scale = (t[mid].u - t[lo].u) / (t[hi].u - t[lo].u);
VecComb(1.0 - scale, p[lo], scale, p[hi], &base);
/*
* v axis extends from computed basepoint to
* middle vertex -- but in which direction?
*/
if (t[mid].v < ((1.0 - scale)*t[lo].v + scale*t[hi].v))
VecSub(base, p[mid], dpdv);
else
VecSub(p[mid], base, dpdv);
(void)VecNormalize(dpdv);
}
/* sort v coordinates */
if (t[2].v > t[1].v) {
if (t[1].v > t[0].v) {
hi = 2; mid = 1; lo = 0;
} else if (t[2].v > t[0].v) {
hi = 2; mid = 0; lo = 1;
} else {
hi = 0; mid = 2; lo = 1;
}
} else {
if (t[2].v > t[0].v) {
hi = 1; mid = 2; lo = 0;
} else if (t[1].v > t[0].v) {
hi = 1; mid = 0; lo = 2;
} else {
hi = 0; mid = 1; lo = 2;
}
}
if (fabs(t[hi].v - t[lo].v) < EPSILON) {
/* degenerate axis */
dpdu->x = dpdu->y = dpdu->z = 0.;
} else {
/*
* Given v coordinates of vertices forming the
* 'long' edge, find where 'middle'
* vertex falls on that edge given its v coordinate.
*/
scale = (t[mid].v - t[lo].v) / (t[hi].v - t[lo].v);
VecComb(1.0 - scale, p[lo], scale, p[hi], &base);
/*
* u axis extends from computed basepoint to
* middle vertex -- but in which direction?
*/
if (t[mid].u < ((1.0 - scale)*t[lo].u + scale*t[hi].u))
VecSub(base, p[mid], dpdu);
else
VecSub(p[mid], base, dpdu);
(void)VecNormalize(dpdu);
}
}
void
TriangleMethodRegister(meth)
UserMethodType meth;
{
if (iTriangleMethods)
iTriangleMethods->user = meth;
}